// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <vector>
#include "base/bind.h"
#include "base/cxx17_backports.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/origin_trials/scoped_test_origin_trial_policy.h"
#include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
#include "third_party/blink/renderer/core/html/html_meta_element.h"
#include "third_party/blink/renderer/core/html/html_script_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/script/classic_script.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"

namespace blink {
namespace {

// Generated by:
//  tools/origin_trials/generate_token.py --version 3 --expire-days 3650 \
//      https://documenttransition.test DocumentTransition
// Token details:
//   Version: 3
//   Origin: https://documenttransition.test:443
//   Is Subdomain: None
//   Is Third Party: None
//   Usage Restriction: None
//   Feature: DocumentTransition
//   Expiry: 1935769331 (2031-05-05 17:42:11 UTC)
//   Signature (Base64):
//   E22eLb6pyd0TWZLONxJOIxUa86AFkIFgijoM1ZKA2k9440xSvhBC5rzyhGjDUNWUQuaIE1yE4811ogD7ZNaFDA==

constexpr char kDocumentTransitionToken[] =
    "AxNtni2+qcndE1mSzjcSTiMVGvOgBZCBYIo6DNWSgNpPeONMUr4QQua88oRow1DVlE"
    "LmiBNchOPNdaIA+2TWhQwAAABoeyJvcmlnaW4iOiAiaHR0cHM6Ly9kb2N1bWVudHRy"
    "YW5zaXRpb24udGVzdDo0NDMiLCAiZmVhdHVyZSI6ICJEb2N1bWVudFRyYW5zaXRpb2"
    "4iLCAiZXhwaXJ5IjogMTkzNTc2OTMzMX0=";

::testing::AssertionResult CheckTokenControlsFeature(const char* trial_token) {
  blink::ScopedTestOriginTrialPolicy using_test_keys;

  DummyPageHolder page_holder;
  Document& document = page_holder.GetDocument();

  // Clear the security origin and set a secure one, recomputing the security
  // state.
  SecurityContext& security_context =
      page_holder.GetFrame().DomWindow()->GetSecurityContext();
  security_context.SetSecurityOriginForTesting(nullptr);
  security_context.SetSecurityOrigin(
      SecurityOrigin::CreateFromString("https://documenttransition.test"));
  EXPECT_EQ(security_context.GetSecureContextMode(),
            SecureContextMode::kSecureContext);

  // Enable scripts so that <script> is not ignored.
  page_holder.GetFrame().GetSettings()->SetScriptEnabled(true);

  EXPECT_FALSE(RuntimeEnabledFeatures::DocumentTransitionEnabled(
      document.GetExecutionContext()));

  HTMLMetaElement* meta =
      MakeGarbageCollected<HTMLMetaElement>(document, CreateElementFlags());
  meta->setAttribute(html_names::kHttpEquivAttr, "Origin-Trial");
  meta->setAttribute(html_names::kContentAttr, trial_token);
  document.head()->appendChild(meta);

  v8::HandleScope scope(v8::Isolate::GetCurrent());
  auto value =
      ClassicScript::CreateUnspecifiedScript("!!document.documentTransition")
          ->RunScriptAndReturnValue(document.domWindow());

  NonThrowableExceptionState exceptionState;
  bool enabled = ToBoolean(scope.GetIsolate(), value, exceptionState);

  EXPECT_EQ(RuntimeEnabledFeatures::DocumentTransitionEnabled(
                document.GetExecutionContext()),
            enabled);

  return enabled
             ? ::testing::AssertionSuccess() << "feature enabled by token"
             : ::testing::AssertionFailure() << "feature not enabled by token";
}

TEST(DocumentTransitionOriginTrialTest, ValidTokenControlsFeature) {
  EXPECT_TRUE(CheckTokenControlsFeature(kDocumentTransitionToken));
}

TEST(DocumentTransitionOriginTrialTest, InvalidTokenIsIgnored) {
  EXPECT_FALSE(CheckTokenControlsFeature("invalid token"));
}

}  // namespace
}  // namespace blink
